#ifndef _SCHED_H
#define _SCHED_H

#define NR_TASKS 64
#define HZ 100

#define FIRST_TASK task[0]
#define LAST_TASK task[NR_TASKS-1]

#include <linux/head.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <signal.h>

#if (NR_OPEN > 32)
#error "Currently the close-on-exec-flags are in one word, max 32 files/proc"
#endif

#define TASK_RUNNING		0
#define TASK_INTERRUPTIBLE	1
#define TASK_UNINTERRUPTIBLE	2
#define TASK_ZOMBIE		3
#define TASK_STOPPED		4

#ifndef NULL
#define NULL ((void *) 0)
#endif

extern int copy_page_tables(unsigned long from, unsigned long to, long size);
extern int free_page_tables(unsigned long from, unsigned long size);

extern void sched_init(void);
extern void schedule(void);
extern void trap_init(void);
extern void panic(const char * str);
extern int tty_write(unsigned minor,char * buf,int count);

typedef int (*fn_ptr)();

struct i387_struct {
	long	cwd;
	long	swd;
	long	twd;
	long	fip;
	long	fcs;
	long	foo;
	long	fos;
	long	st_space[20];	/* 8*10 bytes for each FP-reg = 80 bytes */
};

// 在系统初始化时，会为每个进程分配TSS，并将其描述符添加到GDT中。
struct tss_struct {
	long	back_link;	/* 16 high bits zero */ // 指向先前任务的TSS选择子，用于任务链接
	long	esp0;  // 特权级0的栈指针
	long	ss0;		/* 16 high bits zero */ // 特权级0的栈段选择子
	long	esp1;  // 特权级1的栈指针
	long	ss1;		/* 16 high bits zero */ // 特权级1的栈段选择子
	long	esp2;  // 特权级2的栈指针
	long	ss2;		/* 16 high bits zero */ // 特权级2的栈段选择子
	long	cr3;  // 页目录基址寄存器，指向页表
	long	eip;  // 指令指针，保存当前执行指令的地址
	long	eflags;  // 标志寄存器，保存当前的标志状态
	long	eax, ecx, edx, ebx;  // 通用寄存器
	long	esp;  // 栈指针
	long	ebp;  // 基址指针
	long	esi;  // 源变址寄存器
	long	edi;  // 目的变址寄存器
	long	es;		/* 16 high bits zero */ // 段寄存器
	long	cs;		/* 16 high bits zero */ // 代码段寄存器
	long	ss;		/* 16 high bits zero */ // 栈段寄存器
	long	ds;		/* 16 high bits zero */ // 数据段寄存器
	long	fs;		/* 16 high bits zero */ // 附加段寄存器
	long	gs;		/* 16 high bits zero */ // 附加段寄存器
	long	ldt;		/* 16 high bits zero */ // 局部描述符表选择子
	long	trace_bitmap;	/* bits: trace 0, bitmap 16-31 */ // 位图，包含调试和I/O权限
	struct i387_struct i387; // 保存浮点单元（FPU）状态
};

struct task_struct {
/* these are hardcoded - don't touch */
    long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
                   /* 进程的运行状态。-1 表示不可运行，0 表示可运行，大于0 表示已停止 */
    long counter;  /* 进程的时间片计数器。用于调度算法，决定进程的运行时间 */
    long priority; /* 进程的优先级。用于调度算法，决定进程的调度优先级 */
    long signal;   /* 进程的信号位图，表示接收到的信号 */
    struct sigaction sigaction[32]; /* 每个信号的处理结构体数组，包含信号处理程序等信息 */
    long blocked;  /* bitmap of masked signals */
                   /* 信号屏蔽位图，表示哪些信号被屏蔽 */

/* various fields */
/* 各种字段 */
    int exit_code; /* 进程的退出码 */
    unsigned long start_code, end_code, end_data, brk, start_stack;
                   /* 内存管理信息，分别表示代码段起始地址、代码段结束地址、数据段结束地址、堆的当前末端、栈的起始地址 */
    long pid;      /* 进程的标识符（PID） */
    long father;   /* 父进程的PID */
    long pgrp;     /* 进程组ID */
    long session;  /* 会话ID */
    long leader;   /* 会话首领的PID */
    unsigned short uid, euid, suid; /* 用户ID、有效用户ID、保存的用户ID */
    unsigned short gid, egid, sgid; /* 组ID、有效组ID、保存的组ID */
    long alarm;    /* 报警定时器值 */
    long utime, stime, cutime, cstime;
                   /* 用户态时间、系统态时间、子进程用户态时间、子进程系统态时间 */
    long start_time; /* 进程的启动时间 */
    unsigned short used_math; /* 是否使用过浮点运算单元的标志 */

/* file system info */
/* 文件系统信息 */
    int tty;       /* -1 if no tty, so it must be signed */
                   /* 终端号，-1 表示没有终端 */
    unsigned short umask; /* 文件创建掩码 */
    struct m_inode *pwd; /* 当前工作目录的索引节点 */
    struct m_inode *root; /* 根目录的索引节点 */
    struct m_inode *executable; /* 可执行文件的索引节点 */
    unsigned long close_on_exec; /* 执行exec时需要关闭的文件描述符位图 */
    struct file *filp[NR_OPEN]; /* 进程打开的文件表，指向文件结构体数组 */

/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */
/* 进程的局部描述符表，包含三个描述符，分别为0 - 保留、1 - 代码段、2 - 数据段和栈段 */
    struct desc_struct ldt[3];

/* tss for this task */
/* 进程的任务状态段，用于保存任务切换时的状态信息 */
    struct tss_struct tss;
};


/*
 *  INIT_TASK is used to set up the first task table, touch at
 * your own risk!. Base=0, limit=0x9ffff (=640kB)
 */
#define INIT_TASK \
/* state etc */	{ 0,15,15, \
/* signals */	0,{{},},0, \
/* ec,brk... */	0,0,0,0,0,0, \
/* pid etc.. */	0,-1,0,0,0, \
/* uid etc */	0,0,0,0,0,0, \
/* alarm */	0,0,0,0,0,0, \
/* math */	0, \
/* fs info */	-1,0022,NULL,NULL,NULL,0, \
/* filp */	{NULL,}, \
	{ \
		{0,0}, \
/* ldt */	{0x9f,0xc0fa00}, \
		{0x9f,0xc0f200}, \
	}, \
/*tss*/	{0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\
	 0,0,0,0,0,0,0,0, \
	 0,0,0x17,0x17,0x17,0x17,0x17,0x17, \
	 _LDT(0),0x80000000, \
		{} \
	}, \
}

extern struct task_struct *task[NR_TASKS];
extern struct task_struct *last_task_used_math;
extern struct task_struct *current;
extern long volatile jiffies;
extern long startup_time;

#define CURRENT_TIME (startup_time+jiffies/HZ)

extern void add_timer(long jiffies, void (*fn)(void));
extern void sleep_on(struct task_struct ** p);
extern void interruptible_sleep_on(struct task_struct ** p);
extern void wake_up(struct task_struct ** p);

/*
 * Entry into gdt where to find first TSS. 0-nul, 1-cs, 2-ds, 3-syscall
 * 4-TSS0, 5-LDT0, 6-TSS1 etc ...
 */
#define FIRST_TSS_ENTRY 4
#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
#define ltr(n) __asm__("ltr %%ax"::"a" (_TSS(n)))
#define lldt(n) __asm__("lldt %%ax"::"a" (_LDT(n)))
#define str(n) \
__asm__("str %%ax\n\t" \
	"subl %2,%%eax\n\t" \
	"shrl $4,%%eax" \
	:"=a" (n) \
	:"a" (0),"i" (FIRST_TSS_ENTRY<<3))
/*
 *	switch_to(n) should switch tasks to task nr n, first
 * checking that n isn't the current task, in which case it does nothing.
 * This also clears the TS-flag if the task we switched to has used
 * tha math co-processor latest.
 */
// 输入输出部分：
	// :: 表示没有输出操作数。
	// "m" (*&__tmp.a) 和 "m" (*&__tmp.b) 是输入操作数，表示内存地址。
	// "d" (_TSS(n)) 将 _TSS(n) 的值放入 DX 寄存器。
	// "c" ((long) task[n]) 将 (long) task[n] 的值放入 ECX 寄存器。
// 内联汇编部分：
	// cmpl %%ecx,_current 比较 ECX 寄存器中的值与 _current（当前任务的指针）
	// movw %%dx,%1 将 DX 寄存器中的值（新任务的 TSS 段选择子：_TSS(n)）移动到 __tmp.b 中
	// xchgl %%ecx,_current：交换 ECX 中的新任务指针和 _current 中的当前任务指针。此时，_current 中保存的是新任务指针，ECX 中保存的是旧任务指针（即上一个任务指针）
	// ljmp %0：使用远跳转指令跳转到新的任务状态段。%0 是 *&__tmp.a，表示新的任务的 TSS 段选择子
	// cmpl %%ecx,_last_task_used_math：比较 ECX（旧任务指针）和 _last_task_used_math。_last_task_used_math 保存的是上一个使用协处理器的任务指针。
	// jne 1f：如果不相等，跳转到标签 1 结束宏执行。
	// clts：如果相等，清除任务切换标志（TS 位），允许新任务使用协处理器。
#define switch_to(n) {\
struct {long a,b;} __tmp; \
__asm__("cmpl %%ecx,_current\n\t" \
	"je 1f\n\t" \
	"movw %%dx,%1\n\t" \
	"xchgl %%ecx,_current\n\t" \
	"ljmp %0\n\t" \
	"cmpl %%ecx,_last_task_used_math\n\t" \
	"jne 1f\n\t" \
	"clts\n" \
	"1:" \
	::"m" (*&__tmp.a),"m" (*&__tmp.b), \
	"d" (_TSS(n)),"c" ((long) task[n])); \
}

#define PAGE_ALIGN(n) (((n)+0xfff)&0xfffff000)

#define _set_base(addr,base) \
__asm__("movw %%dx,%0\n\t" \
	"rorl $16,%%edx\n\t" \
	"movb %%dl,%1\n\t" \
	"movb %%dh,%2" \
	::"m" (*((addr)+2)), \
	  "m" (*((addr)+4)), \
	  "m" (*((addr)+7)), \
	  "d" (base) \
	:"dx")

#define _set_limit(addr,limit) \
__asm__("movw %%dx,%0\n\t" \
	"rorl $16,%%edx\n\t" \
	"movb %1,%%dh\n\t" \
	"andb $0xf0,%%dh\n\t" \
	"orb %%dh,%%dl\n\t" \
	"movb %%dl,%1" \
	::"m" (*(addr)), \
	  "m" (*((addr)+6)), \
	  "d" (limit) \
	:"dx")

#define set_base(ldt,base) _set_base( ((char *)&(ldt)) , base )
#define set_limit(ldt,limit) _set_limit( ((char *)&(ldt)) , (limit-1)>>12 )

#define _get_base(addr) ({\
unsigned long __base; \
__asm__("movb %3,%%dh\n\t" \
	"movb %2,%%dl\n\t" \
	"shll $16,%%edx\n\t" \
	"movw %1,%%dx" \
	:"=d" (__base) \
	:"m" (*((addr)+2)), \
	 "m" (*((addr)+4)), \
	 "m" (*((addr)+7))); \
__base;})

#define get_base(ldt) _get_base( ((char *)&(ldt)) )

#define get_limit(segment) ({ \
unsigned long __limit; \
__asm__("lsll %1,%0\n\tincl %0":"=r" (__limit):"r" (segment)); \
__limit;})

#endif
